/*===========================================================================
  Copyright (C) 2011-2014 by the Okapi Framework contributors
-----------------------------------------------------------------------------
  This library is free software; you can redistribute it and/or modify it 
  under the terms of the GNU Lesser General Public License as published by 
  the Free Software Foundation; either version 2.1 of the License, or (at 
  your option) any later version.

  This library is distributed in the hope that it will be useful, but 
  WITHOUT ANY WARRANTY; without even the implied warranty of 
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 
  General Public License for more details.

  You should have received a copy of the GNU Lesser General Public License 
  along with this library; if not, write to the Free Software Foundation, 
  Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

  See also the full LGPL text here: http://www.gnu.org/copyleft/lesser.html
===========================================================================*/

package net.sf.okapi.applications.lynx;

import java.io.File;
import java.security.InvalidParameterException;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;

import net.sf.okapi.lib.xliff2.URIParser;
import net.sf.okapi.lib.xliff2.core.Unit;
import net.sf.okapi.lib.xliff2.reader.Event;
import net.sf.okapi.lib.xliff2.reader.XLIFFReader;
import net.sf.okapi.lib.xliff2.validation.Issue;
import net.sf.okapi.lib.xliff2.validation.Validation;

public class Main {

	protected final static int CMD_READ = 0;
	protected final static int CMD_REWRITE = 1;
	protected final static int CMD_HTMLPREVIEW = 2;
	protected final static int CMD_VALIDATE = 3;
	protected final static int CMD_FINDURI = 4;
	protected final static int CMD_LISTPREFIXES = 5;
	protected final static int CMD_VERIFYREFS = 6;
	protected final static int CMD_EXECUTE = 7;
	protected final static int CMD_PROCESSVALRULES = 8;
	protected final static int CMD_TESTITS = 9;

	public static void main (String[] originalArgs) {
		boolean showTrace = false;
		try {
			// Remove all empty arguments
			// This is to work around the "$1" issue in bash
			ArrayList<String> args = new ArrayList<String>();
			for ( String tmp : originalArgs ) {
				if ( tmp.length() > 0 ) {
					if ( tmp.equals("-trace") ) showTrace = true;
					else args.add(tmp);
				}
			}
			
			// Launch the dialog if needed
			if ( args.size() == 0 ) {
				MainDialog.start();
				return;
			}
	
			Main prog = new Main();
			prog.printBanner();
	
			if ( args.contains("-?") ) {
				prog.printUsage();
				return; // Overrides all arguments 
			}
			if ( args.contains("-h") || args.contains("--help") || args.contains("-help") ) {
				prog.printUsage();
				return; // Overrides all arguments
			}
			if ( args.contains("-i") || args.contains("--info")  || args.contains("-info") ) {
				prog.printInfo();
				return; // Overrides all arguments 
			}
			
			int command = CMD_VALIDATE;
			boolean verbose = false;
			boolean pseudoTranslate = false;
			boolean join = false;
			boolean joinAsMerger = false; 
			boolean segment = false;
			boolean removeAnnotations = false;
			boolean removeExtensions = false;
			boolean removeModules = false;
			String moduleSuffix = null;
			boolean showSource = false;
			int example = 0;
			String uriFrag = null;
			String itsDC = null;
			File extraPrefixesFile = null;
			boolean withOriginalData = true;
			ArrayList<File> inputFiles = new ArrayList<File>();
			
			for ( int i=0; i<args.size(); i++ ) {
				String arg = args.get(i);
				if ( arg.equals("-r") ) {
					command = CMD_READ;
				}
				else if ( arg.equals("-v") ) {
					command = CMD_VALIDATE;
				}
				else if ( arg.equals("-rw") ) {
					command = CMD_REWRITE;
				}
				else if ( arg.equals("-fr") ) {
					command = CMD_FINDURI;
					if ( i+1 >= args.size() ) {
						throw new InvalidParameterException("-fr command is missing the reference.");
					}
					i++; uriFrag = args.get(i);
					if ( !uriFrag.startsWith("#") ) {
						throw new InvalidParameterException("The reference must start with a '#': "+uriFrag);
					}
				}
				else if ( arg.equals("-lp") ) {
					command = CMD_LISTPREFIXES;
				}
				else if ( arg.equals("-vr") ) {
					command = CMD_VERIFYREFS;
				}
				else if ( arg.equals("-pvr") ) {
					command = CMD_PROCESSVALRULES;
				}
				else if ( arg.equals("-its") ) {
					command = CMD_TESTITS;
					if ( i+1 >= args.size() ) {
						throw new InvalidParameterException("-its command is missing the data category to test.");
					}
					i++; itsDC = args.get(i);
				}
				else if ( arg.equals("-prefixes") ) {
					if ( i+1 >= args.size() ) {
						throw new InvalidParameterException("-prefixes option is missing the file path.");
					}
					i++; extraPrefixesFile = new File(args.get(i));
				}
				else if ( arg.equals("-pseudo") ) {
					pseudoTranslate = true;
				}
				else if ( arg.equals("-seg") ) {
					segment = true;
				}
				else if ( arg.equals("-join1") ) {
					join = true;
				}
				else if ( arg.equals("-join2") ) {
					join = true;
					joinAsMerger = true;
				}
				else if ( arg.equals("-ra") ) {
					removeAnnotations = true;
				}
				else if ( arg.equals("-rx") ) {
					removeExtensions = true;
				}
				else if ( arg.equals("-rm") ) {
					removeModules = true;
					if ( i+1 >= args.size() ) {
						throw new InvalidParameterException("-rm option is missing its parameter.");
					}
					i++; moduleSuffix = args.get(i);
					if ( moduleSuffix.equals("all") ) moduleSuffix = null;
				}
				else if ( arg.equals("-html") ) {
					command = CMD_HTMLPREVIEW;
				}
				else if ( arg.equals("-x") ) {
					new Snippets().list();
				}
				else if ( arg.startsWith("-x") ) {
					try {
						example = Integer.parseInt(arg.substring(2));
						command = CMD_EXECUTE;
					}
					catch ( NumberFormatException e ) {
						System.out.println(String.format("Invalid example number: '%s'.", arg));
					}
				}
				else if ( arg.equals("-source") ) {
					showSource = true;
				}
				else if ( arg.equals("-verbose") ) {
					verbose = true;
				}
				else if ( arg.equals("-rd") ) {
					withOriginalData = false;
				}
				else if ( arg.startsWith("-") ) {
					System.out.println("Invalid option: "+arg);
					prog.printUsage();
					return;
				}
				else {
					prog.addToInputList(inputFiles, arg);
				}
			}
			
			switch ( command ) {
			case CMD_HTMLPREVIEW:
				HtmlPreview prev = new HtmlPreview();
				prev.process(inputFiles);
				break;
			case CMD_VALIDATE:
				prog.validate(showTrace, extraPrefixesFile, inputFiles);
				break;
			case CMD_PROCESSVALRULES:
				prog.processValidationRules(showTrace, extraPrefixesFile, inputFiles);
				break;
			case CMD_FINDURI:
				new FragmentFinder(extraPrefixesFile).findFragment(inputFiles, uriFrag);
				break;
			case CMD_LISTPREFIXES:
				new FragmentFinder(extraPrefixesFile).listPrefixes();
				break;
			case CMD_VERIFYREFS:
				new FragmentFinder(extraPrefixesFile).verifyReferences(inputFiles);
				break;
			case CMD_EXECUTE:
				new Snippets().execute(example, showSource);
				break;
			case CMD_TESTITS:
				new ITSTest(true, showTrace, itsDC).process(inputFiles);
				break;
			default:
				Rewriter rewriter = new Rewriter(
					(command == CMD_READ) ? true : verbose,
					(command == CMD_REWRITE),
					pseudoTranslate,
					join,
					joinAsMerger,
					segment,
					removeAnnotations,
					removeExtensions,
					removeModules,
					moduleSuffix,
					withOriginalData);
				rewriter.process(inputFiles);
				break;
			}
		}
		catch ( Throwable e ) {
			if ( showTrace ) {
				e.printStackTrace();
			}
			else {
				System.out.println("ERROR (" + e.getClass().getName()+")");
				System.out.println(e.getLocalizedMessage());
			}
			System.exit(1);
		}
	}
	
	private void addToInputList (ArrayList<File> inputFiles,
		String path)
	{
		// Get the directory
		File file = new File(path);
//		String mask = file.getName();
//		// Handle wild-card pattern if needed
//		if ( mask.startsWith("*") ) {
//			DirectoryStream<Path> paths;
//			try {
//				paths = Files.newDirectoryStream(Paths.get(file.toURI()), mask);
//			}
//			catch ( IOException e ) {
//				throw new RuntimeException(e.getLocalizedMessage());
//			}
//			for ( Path tmp : paths ) {
//				inputFiles.add(tmp.toFile());
//			}
//		}
//		else {
			inputFiles.add(file);
//		}
	}
	
	private void validate (boolean showTrace,
		File prefixesFile,
		ArrayList<File> inputFiles)
	{
		URIParser uriParser = null;
		if ( prefixesFile != null ) {
			uriParser = new URIParser(prefixesFile);
		}
		XLIFFReader reader = new XLIFFReader(XLIFFReader.VALIDATION_MAXIMAL, uriParser);

		int errCount = 0;
		for ( File file : inputFiles ) {
			try {
				System.out.println("Input: "+file.getAbsolutePath());
				reader.open(file);
				System.out.println("Schemas validation successful.");
				while ( reader.hasNext() ) {
					reader.next();
				}
				System.out.println("Core processing validation successful.");
			}
			catch ( Throwable e ) {
				System.out.println("ERROR (" + e.getClass().getName()+")");
				System.out.println(e.getLocalizedMessage());
				errCount++;
				if ( showTrace ) e.printStackTrace();
			}
			finally {
				if ( reader != null ) reader.close();
			}
		}
		if ( errCount > 0 ) {
			throw new RuntimeException(String.format("One or more errors detected (count=%d).", errCount));
		}
	}

	private void processValidationRules (boolean showTrace,
		File prefixesFile,
		ArrayList<File> inputFiles)
	{
		int errCount = 0;
		URIParser uriParser = null;
		if ( prefixesFile != null ) {
			uriParser = new URIParser(prefixesFile);
		}
		
		try ( XLIFFReader reader = new XLIFFReader(XLIFFReader.VALIDATION_MAXIMAL, uriParser) ) {
			for ( File file : inputFiles ) {
				try {
					System.out.println("Input: "+file.getAbsolutePath());
					reader.open(file);
					String fileId = null;
					while ( reader.hasNext() ) {
						Event event = reader.next();
						if ( event.isStartFile() ) {
							fileId = event.getStartFileData().getId();
						}
						if ( !event.isUnit() ) continue;
						// Process validation rules in the unit
						Unit unit = event.getUnit();
						if ( !unit.hasValidation() ) continue;
						Validation val = unit.getValidation();
						List<Issue> list = val.processRules(unit, fileId);
						if (( list == null ) || list.isEmpty() ) continue;
						System.out.println("== Validation module errors File-ID="+fileId+", Unit-ID="+unit.getId()+":");
						for ( Issue issue : list ) {
							System.out.println("- " + issue.getCode()+"\n"
								+ issue.getText());
							errCount++;
						}
					}
				}
				catch ( Throwable e ) {
					System.out.println("ERROR (" + e.getClass().getName()+")");
					System.out.println(e.getLocalizedMessage());
					errCount++;
					if ( showTrace ) e.printStackTrace();
				}
			}
		}
		if ( errCount > 0 ) {
			throw new RuntimeException(String.format("One or more errors detected (count=%d).", errCount));
		}
	}

	private void printBanner () {
		System.out.println("-------------------------------------------------------------------------------"); //$NON-NLS-1$
		System.out.println("Okapi Lynx - Validation and Testing Tool for XLIFF 2");
		// The version will show as 'null' until the code is build as a JAR.
		System.out.println(String.format("Library version: %s", getClass().getPackage().getImplementationVersion()));
		System.out.println("-------------------------------------------------------------------------------"); //$NON-NLS-1$
	}
	
	private void printUsage () {
		System.out.println("Shows this screen: -? or -h");
		System.out.println("Shows version and other information: -i or -info");
		System.out.println("Perform schema and processing validation on the input file (default command):");
		System.out.println("   -v [-prefixes path] inputFile1 [inputFile2...]");
		System.out.println("Reads the input file and displays the parsed results:");
		System.out.println("   -r [-prefixes path] inputFile1 [inputFile2...]");
		System.out.println("Rewrites the input file to a new file (same name with an extra '.out'):");
		System.out.println("   -rw [-verbose] [-rd] [-ra] [-rx] [-rm <nsSuffix>|all] [-pseudo]");
		System.out.println("       [-prefixes path] [-join1|-join2] [-seg] inputFile1 [inputFile2...]"); 
		System.out.println("Find a reference in the input file:");
		System.out.println("   -fr <ref> [-prefixes path] inputFile1 [inputFile2...]"); 
		System.out.println("Verifies the syntax of the ref attributes in annotations:");
		System.out.println("   -vr [-prefixes path] inputFile1 [inputFile2...]"); 
		System.out.println("List all available prefixes for fragment identifiers:");
		System.out.println("   -lp [-prefixes path]");
		System.out.println("Process the rules of the Validation module:");
		System.out.println("   -pvr [-prefixes path] inputFile1 [inputFile2...]");
		System.out.println("Test the ITS module:");
		System.out.println("   -its <dataCategory> inputFile1 [inputFile2...]");
		System.out.println("Creates an HTML preview (same name with an extra '.html'):");
		System.out.println("   -html [-prefixes path] inputFile1 [inputFile2...]"); 
		System.out.println("Executes the snippet example number <N>:");
		System.out.println("   -x<N> [-source]"); 
		System.out.println("Lists all snippet examples available:");
		System.out.println("   -x"); 
	}
	
	private void printInfo () {
		Runtime rt = Runtime.getRuntime();
		rt.runFinalization();
		rt.gc();
		System.out.println("Java version: " + System.getProperty("java.version")); //$NON-NLS-1$
		System.out.println(String.format("Platform: %s, %s, %s",
			System.getProperty("os.name"), //$NON-NLS-1$ 
			System.getProperty("os.arch"), //$NON-NLS-1$
			System.getProperty("os.version"))); //$NON-NLS-1$
		NumberFormat nf = NumberFormat.getInstance();
		System.out.println(String.format("Java VM memory: free=%s KB, total=%s KB", //$NON-NLS-1$
			nf.format(rt.freeMemory()/1024),
			nf.format(rt.totalMemory()/1024)));
		System.out.println("-------------------------------------------------------------------------------"); //$NON-NLS-1$
	}

}
